<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <div id="mountNode"></div>
  </body>
  <script src="./assets/dagre.js"></script>
  <script src="../build/g6.js"></script>
  <script>
    /**
     * node 特殊属性
     */
    const nodeExtraAttrs = {
      begin: {
        fill: '#9FD4FB',
      },
      end: {
        fill: '#C2E999',
      },
    };

    const data = {
      nodes: [
        {
          id: '1',
          label: '请求回放1（开始）',
          type: 'begin',
        },
        {
          id: '2',
          label: '交易创建',
        },
        {
          id: '3',
          label: '请求回放3',
        },
        {
          id: '4',
          label: '请求回放4',
        },
        {
          id: '5',
          label: '请求回放5',
        },
        {
          id: '6',
          label: '请求回放6',
        },
        {
          id: '7',
          label: '请求回放2（结束）',
          type: 'end',
        },
      ],
      edges: [
        {
          source: '1',
          target: '2',
        },
        {
          source: '1',
          target: '3',
        },
        {
          source: '2',
          target: '5',
        },
        {
          source: '5',
          target: '6',
        },
        {
          source: '6',
          target: '7',
        },
        {
          source: '3',
          target: '4',
        },
        {
          source: '4',
          target: '7',
        },
      ],
    };

    /**
     * 自定义节点
     */
    G6.registerNode(
      'rect-node',
      {
        drawShape: function drawShape(cfg, group) {
          var rect = group.addShape('rect', {
            attrs: {
              x: -75,
              y: -25,
              width: 150,
              height: 50,
              radius: 4,
              fill: '#FFD591',
              fillOpacity: 1,
              ...nodeExtraAttrs[cfg.type],
            },
          });
          return rect;
        },
        getAnchorPoints: function getAnchorPoints() {
          return [
            [0, 0.5],
            [1, 0.5],
          ];
        },
      },
      'single-shape',
    );

    /**
     * 自定义带箭头的贝塞尔曲线 edge
     */
    G6.registerEdge('line-with-point', {
      itemType: 'edge',
      draw: function draw(cfg, group) {
        const startPoint = cfg.startPoint;
        const endPoint = cfg.endPoint;
        const centerPoint = {
          x: (startPoint.x + endPoint.x) / 2,
          y: (startPoint.y + endPoint.y) / 2,
        };
        // 控制点坐标
        const controlPoint = {
          x: (startPoint.x + centerPoint.x) / 2,
          y: startPoint.y,
        };

        console.log(cfg, startPoint, endPoint);

        // 为了更好的展示效果, 对称贝塞尔曲线需要连到箭头根部
        const path = group.addShape('path', {
          attrs: {
            path: [
              ['M', startPoint.x, startPoint.y],
              ['Q', controlPoint.x + 8, controlPoint.y, centerPoint.x, centerPoint.y],
              ['T', endPoint.x - 8, endPoint.y],
              ['L', endPoint.x, endPoint.y],
            ],
            stroke: '#ccc',
            lineWidth: 1.6,
            endArrow: {
              path: 'M 4,0 L -4,-4 L -4,4 Z',
              d: 4,
            },
          },
        });

        // 计算贝塞尔曲线上的中心点
        // 参考资料 https://stackoverflow.com/questions/54216448/how-to-find-a-middle-point-of-a-beizer-curve
        // const lineCenterPoint = {
        //   x:
        //     (1 / 8) * startPoint.x +
        //     (3 / 8) * (controlPoint.x + 8) +
        //     (3 / 8) * centerPoint.x +
        //     (1 / 8) * (endPoint.x - 8),
        //   y:
        //     (1 / 8) * startPoint.y +
        //     (3 / 8) * controlPoint.y +
        //     (3 / 8) * centerPoint.y +
        //     (1 / 8) * endPoint.y
        // };

        // 在贝塞尔曲线中心点上添加圆形
        const { source, target } = cfg;
        group.addShape('circle', {
          attrs: {
            id: `statusNode${source}-${target}`,
            r: 6,
            x: centerPoint.x,
            y: centerPoint.y,
            fill: cfg.active ? '#AB83E4' : '#ccc',
          },
        });

        return path;
      },
    });

    const g = new dagre.graphlib.Graph();
    g.setDefaultEdgeLabel(function() {
      return {};
    });
    g.setGraph({ rankdir: 'LR' });

    data.nodes.forEach(node => {
      g.setNode(node.id + '', { width: 150, height: 50 });
    });

    data.edges.forEach(edge => {
      edge.source = edge.source + '';
      edge.target = edge.target + '';
      g.setEdge(edge.source, edge.target);
    });
    dagre.layout(g);

    let coord;
    g.nodes().forEach((node, i) => {
      coord = g.node(node);
      data.nodes[i].x = coord.x;
      data.nodes[i].y = coord.y;
    });
    g.edges().forEach((edge, i) => {
      coord = g.edge(edge);
      const startPoint = coord.points[0];
      const endPoint = coord.points[coord.points.length - 1];
      data.edges[i].startPoint = startPoint;
      data.edges[i].endPoint = endPoint;
      data.edges[i].controlPoints = coord.points.slice(1, coord.points.length - 1);
    });

    const graph = new G6.Graph({
      container: 'mountNode',
      width: 1000,
      height: 800,
      defaultNode: {
        shape: 'rect-node',
        labelCfg: {
          style: {
            fill: '#fff',
            fontSize: 14,
          },
        },
      },
      defaultEdge: {
        shape: 'line-with-point',
        style: {
          endArrow: true,
          lineWidth: 2,
          stroke: '#ccc',
        },
      },
    });

    graph.data(data);
    graph.render();

    graph.on('edge:click', evt => {
      const { target } = evt;
      const type = target.get('type');
      if (type === 'circle') {
        // 点击边上的circle
        alert('你点击的是边上的圆点');
      }
    });
  </script>
</html>
